#include <iostream>
#include "Log.h"
#include "Consulta.h"
#include "Terminador.h"
#include "AdministradorEstacionamiento.h"

using namespace std;

struct Parametros {
	int cantidadLugares;
	int costoHora;
	int tiempo;
	int cantidadEstacionamientos;
	bool modoDebug;
};

/**
 * Crea el proceso administrador de estacionamientos.
 */
int crearAdministrador(Parametros parametros, Terminador* terminador){
	pid_t pid = fork();
	if (pid == 0){
		AdministradorEstacionamiento admin(parametros.cantidadEstacionamientos, parametros.cantidadLugares, parametros.costoHora);
		if (admin.inicializar() == 0){
			admin.administrarEstacionamientos();
			admin.destruir();
		}
		return 1;
	} else {
		terminador->setPidAdministrador(pid);
	}
	// Solo el proceso padre devuelve 0
	return 0;
}

/**
 * Crea el proceso de consultas.
 */
int crearConsulta(Parametros parametros, Terminador* terminador){
	pid_t pid = fork();
	if (pid == 0){
		Consulta consulta(parametros.cantidadEstacionamientos);
		consulta.iniciarDeteccion();
		return 1;
	} else {
		terminador->setPidConsulta(pid);
	}
	// Solo el proceso padre devuelve 0
	return 0;
}

void imprimirUso(char* argv0, string mensaje){
	cerr << "Parámetros inválidos" << endl;
	cerr << "Uso: " << argv0 << " Lugares Costo Tiempo Estacionamientos [-d | Modo]" << endl;
	cerr << mensaje << endl;
}

int validarParametros(int argc, char* argv[], Parametros* parametros){
	int retorno = 0;

	if (! (argc == 5 || argc == 6)) {
		imprimirUso(argv[0], "Cantidad inválida de parámetros.");
		retorno = -1;
	} else {
		int i = 1;

		int c = atoi(argv[i++]);
		if (c > 0){
			parametros->cantidadLugares = c;
		} else {
			imprimirUso(argv[0], "Cantidad de lugares inválida. Debe ser mayor a 0.");
			retorno = -1;
		}
		
		c = atoi(argv[i++]);
		if (c > 0){
			parametros->costoHora = c;
		}
		else {
			imprimirUso(argv[0], "Costo de la hora inválida. Debe ser mayor a 0.");
			retorno = -1;
		}

		c = atoi(argv[i++]);
		if (c > 0){
			parametros->tiempo = c;
		}
		else {
			imprimirUso(argv[0], "Tiempo de simulación inválido. Debe ser mayor a 0.");
			retorno = -1;
		}
		
		c = atoi(argv[i++]);
		if (c > 0){
			parametros->cantidadEstacionamientos = c;
		} else {
			imprimirUso(argv[0], "Cantidad de estacionamientos inválida. Debe ser mayor a 0.");
			retorno = -1;
		}

		parametros->modoDebug = false;
		if (argc == 6){
			char* modo = argv[i++];
			if (modo[0] == '-' && modo[1] == 'd'){
				parametros->modoDebug = true;
			}
		}
	}

	return retorno;
}

void crearArchivos(){
	Log::getInstance()->loguear("Se crean los archivos usados por las colas de mensajes.");

	crearArchivo(ARCHIVO_COLA_ADMINISTRADOR);
	crearArchivo(ARCHIVO_COLAS_ESTACIONAMIENTOS);
	crearArchivo(ARCHIVO_COLAS_SALIDAS);
}

int main(int argc, char *argv[]) {
	int retorno = 0;

	Parametros parametros;
	if ((retorno = validarParametros(argc, argv, &parametros)) == 0){

		Log::getInstance()->setModoDebug(parametros.modoDebug);
		Log::getInstance()->loguear(string(argv[0])
				+ " " + intToString(parametros.cantidadLugares)
				+ " " + intToString(parametros.costoHora)
				+ " " + intToString(parametros.tiempo)
				+ " " + intToString(parametros.cantidadEstacionamientos));
		Log::getInstance()->imprimir("Comienza la simulación");

		crearArchivos();

		// Se crea el terminador para finalizar la ejecución
		Terminador terminador(parametros.tiempo);
		
		if (crearAdministrador(parametros, &terminador) == 0){
			if (crearConsulta(parametros, &terminador) == 0){
				terminador.ejecutar();
			}
		}

		Log::destruir();
	}

	return retorno;
}
